// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#pragma once

#include "message.hpp"
#include <type_traits>

namespace rotor_light {

struct ActorBase;

namespace details {

template <typename T> struct handler_traits {};

template <typename A, typename M> struct handler_traits<void (A::*)(M &)> {
  using FinalMessage = M;
  using Backend = A;
};

} // namespace details

struct HandlerBase {
  inline HandlerBase() : message_id{0} {}
  inline HandlerBase(MessageTypeId message_id_) : message_id{message_id_} {}
  HandlerBase(HandlerBase &) = delete;
  HandlerBase(HandlerBase &&) = delete;
  virtual void call(Message &, ActorBase &actor) = 0;
  MessageTypeId message_id;
};

static_assert(std::is_trivially_destructible_v<HandlerBase>,
              "trivial destructor");

template <typename Method> struct Handler : HandlerBase {
  using traits = details::handler_traits<Method>;
  using FinalMessage = typename traits::FinalMessage;
  using Backend = typename traits::Backend;

  static_assert(std::is_trivially_destructible_v<FinalMessage>,
                "message should be trivially destructible");

  Handler() : HandlerBase(FinalMessage::type_id), method{nullptr} {}
  Handler(Method &method_)
      : HandlerBase(FinalMessage::type_id), method{method_} {}

  void call(Message &message, ActorBase &actor) override {
    auto &final_message = static_cast<FinalMessage &>(message);
    auto &backend = static_cast<Backend &>(actor);
    (backend.*method)(final_message);
  };

  Method method;
};

} // namespace rotor_light
